#ifndef LENSFLARE_CONFIG_H
#define	LENSFLARE_CONFIG_H

#include <string>
#include <sstream>
#include <list>

namespace Lensflare {
    class Config {
    protected:
		struct Line {
			enum LINE_TYPE {
				NONE,
				SECTION,
				PROPERTY
			};

			LINE_TYPE lineType;
			std::string key;
			std::string value;
			std::string comment;

			Line(LINE_TYPE lineType, const std::string& key, const std::string& value, const std::string& comment) {
				this->lineType = lineType;
				this->key = key;
				this->value = value;
				this->comment = comment;
			}
		};

        std::string fileName;
        std::list<Line> lines;
		std::string globalCommentIndicator;
		std::string globalAssignIndicator;

    protected:
        void Init(const std::string& fileName);

        template <typename ValueType> static void ToString(const ValueType& v, std::string& s);

        template <typename ValueType> static void Parse(const std::string& s, ValueType& v);
		static void Parse(const std::string& s, std::string& v) {
			v.assign(s);
		}

        static void LeftTrim(std::string& s);
        static void RightTrim(std::string& s);
        static void Trim(std::string& s);

    public:
        Config();
        Config(const std::string& fileName);
        virtual ~Config();

        void Save();

        bool KeyExists(const std::string& key);
		bool KeyExists(const std::string& section, const std::string& key);

        template <typename ValueType> void Set(const std::string& key, const ValueType& value);
		template <typename ValueType> void Set(const std::string& section, const std::string& key, const ValueType& value);

        template <typename ValueType> void Get(const std::string& key, ValueType& value);
		template <typename ValueType> void Get(const std::string& section, const std::string& key, ValueType& value);
    };

	template <typename ValueType> void Config::Set(const std::string& section, const std::string& key, const ValueType& value) {
		if(!key.empty()) {
			std::string currentSection = "";
			typename std::list<Line>::iterator it = lines.begin();
			typename std::list<Line>::iterator lastPosMatchingSection = lines.end();
			while (it != lines.end()) {
				Line& line = *it;
				if(line.lineType == Line::SECTION) {
					currentSection = line.key;
				} if (section == currentSection) {
					lastPosMatchingSection = it;
					if(line.lineType == Line::PROPERTY && line.key == key) {
						//Key found. Change property:
						ToString(value, line.value);
						return;
					}
				}
				++it;
			}

			//Key not found. Make new property:
			std::string stringValue;
			ToString(value, stringValue);
			if(lastPosMatchingSection != lines.end()) {
				//Section already exists. Add property to the end of the section:
				lines.insert(++lastPosMatchingSection, 1, Line(Line::PROPERTY, key, stringValue, ""));
			} else {
				//Section does not exist. Add new section with the new property:
				lines.push_back(Line(Line::SECTION, section, "", ""));
				lines.push_back(Line(Line::PROPERTY, key, stringValue, ""));
			}
		}
	}

	template <typename ValueType> void Config::Get(const std::string& section, const std::string& key, ValueType& value) {
		if(!key.empty()) {
			std::string currentSection = "";
			typename std::list<Line>::const_iterator it = lines.begin();
			while (it != lines.end()) {
				const Line& line = *it;
				if(line.lineType == Line::SECTION) {
					currentSection = line.key;
				} else if (line.lineType == Line::PROPERTY && section == currentSection && line.key == key) {
					//Key found. Change output value:
					Parse(line.value, value);
					return;
				}
				++it;
			}
		}
	}

	template <typename ValueType> void Config::Set(const std::string& key, const ValueType& value) {
		Set("", key, value);
	}

    template <typename ValueType> void Config::Get(const std::string& key, ValueType& value) {
		Get("", key, value);
    }

    template <typename ValueType> void Config::ToString(const ValueType& v, std::string& s) {
        std::stringstream ss;
        ss << v;
        s.assign(ss.str());
    }

    template <typename ValueType> void Config::Parse(const std::string& s, ValueType& v) {
        std::stringstream ss;
        ss << s;
        ss >> v;
    }
}

#endif // LENSFLARE_CONFIG_H
